package spl.army;

import java.util.Collections;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;

public class ItemContainer {
	private String name;
	private int amount;
	private int initialAmount;
	
	private HashMap<Sergeant, Integer> aquiredHistory;

	/**
	 * @param nameStr of item
	 * @param nAmount of items
	 */
	public ItemContainer(String nameStr, int nAmount) {
		this.name = nameStr; 
		this.amount = nAmount;
		
		this.initialAmount = this.amount;
		this.aquiredHistory = new HashMap<Sergeant, Integer>(); 
	}
	/**
	 * @return the amount
	 */
	public synchronized int getAmount() {
		return this.amount;
	}
	
	/**
	 * @param amountDiff is the amount the items returned.
	 */
	public synchronized void returnAmount(int amountDiff) {
		this.returnAmount(amountDiff, null);
	}
	
	/**
	 * update item amount to oldAmount + amountDiff
	 * @param amountDiff 
	 * @param consumingSergeant is a pointer to the sergeant that requires the item
	 */
	public synchronized void returnAmount(int amountDiff, Sergeant consumingSergeant) {
		
		// Lock the warehouse, only enough time to update
		// the container.
		Warehouse.wareHouseLock();
		
		this.amount  += amountDiff;
		// Some sergeant returned the item - update it's holdings.
		int totalAmount = amountDiff;
		if (null != consumingSergeant) {
			if (this.aquiredHistory.containsKey(consumingSergeant))
				totalAmount += this.aquiredHistory.get(consumingSergeant).intValue();
			
			if (0 != totalAmount)
				this.aquiredHistory.put(consumingSergeant, totalAmount);
			else 
				this.aquiredHistory.remove(consumingSergeant);
		} else {
			// Added by user
			this.initialAmount += amountDiff;
		}
		
		Warehouse.wareHouseUnlock();
		
		this.notifyAll();
	}
	
	
	/**
	 * try to consume item. 
	 * @param requiredAmount of this item
	 * @param sergeantConsuming is the sergeant consuming the item
	 * @return null when requesting more than available. Item object otherwise
	 */
	public synchronized Item takeAmount(int requiredAmount, Sergeant sergeantConsuming) {		
		if(requiredAmount > this.amount){			
			return null;
		}
		
		// Lock the warehouse, only enough time to update
		// the container.
		this.returnAmount(-1 * requiredAmount, sergeantConsuming);
		
		return new Item(this, requiredAmount, sergeantConsuming);

	}
	
	/**
	 * Call this function ONLY when warehouse is locked!! meaning - generateReport function.
	 * Otherwise, the number will not have a meaning
	 * @note that using this function from unsynchronized code is bad.
	 * @return a collection of the consumers
	 */
	public Map<Sergeant, Integer> getHoldersAssynchronously() {
		return Collections.unmodifiableMap(this.aquiredHistory);
	}
	
	/**
	 * Call this function ONLY when warehouse is locked!! meaning - generateReport function.
	 * Otherwise, the number will not have a meaning
	 * @note that using this function from unsynchronized code is bad.
	 * @return a collection of the consumers
	 */
	public int getAmountAssynchly() {
		return this.amount;
	}
	
	/**
	 * @return string repr of the object.
	 */
	public String toString() {
		StringBuilder sb = new StringBuilder();
		Formatter formatter = new Formatter(sb);
		
		return formatter.format("%s (%d)", this.name, this.initialAmount).toString();
	}
}
